import sys
import os
import time
import pickle

import libtcodpy as libtcod

import console
import menu
import splash
import lights

import objects
import town
import actions
import save_quit
import rock
import utils
import intro
import music
import scores

try:
    import pygame
    NO_SOUND = False
except ImportError:
    NO_SOUND = True

try:
    import psyco
    psyco.full()  # Doesn't work on my 64-bit system :(
except ImportError:
    pass

STR_VERSION = "v0.1.0"
HEX_VERSION = (0x0, 0x1, 0x0)
__version__ = STR_VERSION

SCREEN_WIDTH = 80
SCREEN_HEIGHT = 30
LIMIT_FPS = 30

DEBUG = False

SAVE_FILE = 'save.dat'

class Game(object):
    def __init__(self, font, font_flags, player=None):
        libtcod.console_set_custom_font(font, font_flags)
        self.root = console.RootConsole(SCREEN_WIDTH, SCREEN_HEIGHT, 'RogueBand Hero', False)

        self.splash = intro.Intro(self.root, STR_VERSION)
        self.splash.draw()

        self.splash.log('Loading riffs.')
        self.jukebox = music.Jukebox()
        self.jukebox.event('loading')

        self.town = town.Town(self.splash.log)

        self.splash.log('Spawning player.')
        px, py = utils.find_tile(self.town.map, 'entry')
        self.player = player

        self.splash.log('Lighting lamps.')
        self.fov = lights.FOV(self.town.map, self.town.lights)
        self.static_light = self.fov.static_map
        self.fov_update = True

        self.splash.log('Opening shops')
        self.call_backs = dict()
        self.call_backs['exit'] = save_quit.exit('Left town', (-1, 0), self.jukebox.event_song('exit'))
        self.call_backs['retire'] = save_quit.HallOfFame((0, -1), self.jukebox).retire
        self.call_backs['inne'] = save_quit.save(SAVE_FILE, (-1, 0), self.jukebox.event_song('save'))
        self.call_backs['armor'] = objects.ArmorShop((-1, 0)).shop
        self.call_backs['weapons'] = objects.WeaponShop((1, 0)).shop

        self.splash.log('Setting stage.')
        self.call_backs['hall'] = rock.Game(self.root, (1, 0), self.jukebox, self.splash.log).play

        if player is None:
            #name = raw_input("Enter a name: ")
            name = utils.get_name(self.root)
            self.player = objects.Player(px, py, name)
        else:
            self.splash.log('Loaded: %s' % self.player.name)
            self.splash.log('[PRESS ANY KEY TO START]')
            self.root.wait_for_keypress(True)

        name_notice = '[ %s, Rock Star ]' % self.player.name
        x, y = (SCREEN_WIDTH - len(name_notice)) / 2, SCREEN_HEIGHT - 1
        notice = dict(pos=(x, y), note=splash.Splash(name_notice, key_hit=False, background_alpha=0.0))
        self.town.notices.append(notice)

        self.active = False
        self.draw()
        self.jukebox.event('start')

    def build_static_light_map(self):
        map = []
        for y in range(self.town.height):
            row = []
            for x in range(self.town.width):
                row.append(self.fov.in_static(x, y))
            map.append(row)

        return map

    def draw(self):
        for y, row in enumerate(self.town.map):
            for x, tile in enumerate(row):
                char = ' ' if tile.char is None else tile.char
                self.root.putchar_ex(x, y, char, tile.color, tile.dark)
        self.post_notices()
        self.root.foreground = libtcod.white
        self.root.flush()

    def post_notices(self):
        for notice in self.town.notices:
            x, y = notice['pos']
            note = notice['note']
            note.render(self.root, x=x, y=y)

    def erase(self, item):
        x, y = item.x, item.y
        tile = self.town.map[y][x]
        
        char = ' ' if not tile.char else tile.char
        self.root.putchar_ex(x, y, char, tile.color, tile.dark)

    def update(self, movement):
        call_back = None
        self.erase(self.player)

        self.player.cool_down = max(0, self.player.cool_down - libtcod.sys_get_last_frame_length() * 1000.0)

        if movement:
            new_x = self.player.x + movement[0]
            new_y = self.player.y + movement[1]
            if self.town.map[new_y][new_x].passable:
                self.player.x, self.player.y = new_x, new_y
                self.player.cool_down = self.town.map[new_y][new_x].speed

                self.fov_update = True
                call_back = self.call_backs.get(self.town.map[new_y][new_x].name)

        if self.fov_update:
            self.fov.compute(self.player.x, self.player.y)
            self.fov_update = False

        for y in xrange(self.town.height):
            for x in xrange(self.town.width):
                tile = self.town.map[y][x]
                char = ' ' if not tile.char else tile.char
                if ((x, y) in self.fov) or (self.static_light[y][x]):
                    self.root.putchar_ex(x, y, char, tile.color, tile.light)
                else:
                    self.root.putchar_ex(x, y, char, tile.color, tile.dark)
        self.post_notices()

        if call_back:
            call_back(self.root, self.player)

        self.root.forground = self.player.color
        self.root.putchar(self.player.x, self.player.y, self.player.char, libtcod.BKGND_NONE)
        
        if DEBUG: self.root.print_fps()
        if not self.done_credits:
            fg, bg = self.root.foreground, self.root.background 
            self.done_credits = libtcod.console_credits_render(0, 0, True)
            self.root.foreground = fg
            self.root.background = bg
            # Nice
        self.root.flush()

        if self.player.done:
            self.active = False

    def loop(self):
        self.active = True
        self.draw()
        self.done_credits = False

        while self.active and self.root.open:
            movement = actions.handle_keys(self.root, self.player, self.jukebox)
            self.update(movement)

        if self.player.done != 'Saved':
            pygame.mixer.fadeout(1000)
            scores.score(self.root, self.player)
            pygame.mixer.quit()
        else:
            print "Game saved.  Play again soon!"

def generic_callback(name):
    def callback(console, player):
        window = splash.Splash("You have entered %r" % name, border=True, title=name.title())
        window.show(console, (console.width - window.width) / 2, (console.height - window.height) / 2)
    return callback

def load():
    player = None
    if os.access(SAVE_FILE, os.R_OK): # on!
        player = pickle.load(open(SAVE_FILE, 'rb'))
        try:
            os.remove(SAVE_FILE)
        except:
            player = None
        if os.access(SAVE_FILE, os.R_OK):
            sys.stderr.write("ERROR: Cannot load %s.  May be write protected.\n" % SAVE_FILE)
            player = None

    return player

def start():
    if not NO_SOUND: pygame.mixer.init()

    libtcod.sys_set_fps(LIMIT_FPS)
    font, font_flags = 'fonts/arial12x12.png',        libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD
    #font, font_flags = 'fonts/dejavu12x12_gs_tc.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD
    game = Game(font, font_flags, load())
    game.loop()

    if pygame.mixer.get_busy():
        pygame.mixer.fadeout(1000)
        while pygame.mixer.get_busy():
            time.sleep(0.1)


if __name__ == '__main__':
    start()

